package com.fish.parser;

import com.fish.antlrvisitor.SomeLanguageBaseVisitor;
import com.fish.antlrvisitor.SomeLanguageLexer;
import com.fish.antlrvisitor.SomeLanguageParser;
import com.fish.parser.lang.Class;
import com.fish.parser.lang.Instruction;
import com.fish.parser.lang.Method;
import org.antlr.v4.runtime.CharStream;
import org.antlr.v4.runtime.CharStreams;
import org.antlr.v4.runtime.CommonTokenStream;
import org.antlr.v4.runtime.TokenStream;

import java.util.List;

import static java.util.stream.Collectors.toList;

/**
 * Created by jlutt on 2018-01-22.
 *
 * @author jlutt
 */
public class VisitorOrientedParser {

  public Class parse(String someLangSourceCode) {
    CharStream charStream = CharStreams.fromString(someLangSourceCode);
    SomeLanguageLexer lexer = new SomeLanguageLexer(charStream);
    TokenStream tokens = new CommonTokenStream(lexer);
    SomeLanguageParser parser = new SomeLanguageParser(tokens);

    ClassVisitor classVisitor = new ClassVisitor();
    Class traverseResult = classVisitor.visit(parser.classDeclaration());
    return traverseResult;
  }

  private static class InstructionVisitor extends SomeLanguageBaseVisitor<Instruction> {
    @Override
    public Instruction visitInstruction(SomeLanguageParser.InstructionContext ctx) {
      String instructionName = ctx.getText();
      return new Instruction(instructionName);
    }
  }

  private static class MethodVisitor extends SomeLanguageBaseVisitor<Method> {
    @Override
    public Method visitMethod(SomeLanguageParser.MethodContext ctx) {
      String methodName = ctx.methodName().getText();

      InstructionVisitor instructionVisitor = new InstructionVisitor();

      List<Instruction> instructions = ctx.instruction()
          .stream()
          .map(instruction -> instruction.accept(instructionVisitor))
          .collect(toList());

      return new Method(methodName, instructions);
    }
  }

  private static class ClassVisitor extends SomeLanguageBaseVisitor<Class> {

    @Override
    public Class visitClassDeclaration(SomeLanguageParser.ClassDeclarationContext ctx) {
      String className = ctx.className().getText();
      MethodVisitor methodVisitor = new MethodVisitor();
      List<Method> methods = ctx.method()
          .stream()
          .map(method -> method.accept(methodVisitor))
          .collect(toList());
      return new Class(className, methods);
    }
  }
}
